package com.sczc.esm27.collection.netty;

import com.sczc.esm27.collection.entity.ESM27ResponseMessageEntity;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.List;

/**
 * 解码器
 */
public class DataDecoder extends ByteToMessageDecoder {

    private static final Logger logger = LogManager.getLogger(DataDecoder.class);
    private static final byte FRAME_HEAD = (byte) 0xAD; // 帧头标识
    private static final byte FRAME_TAIL = (byte) 0xFF; // 帧尾标识 FF
    private static final byte ENDING_1 = (byte) 0x0D;   // 结束符 0D
    private static final byte ENDING_2 = (byte) 0x0A;   // 结束符 0A
    private static final int FRAME_LENGTH = 256;        // 整个数据帧长度

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        try {
            logger.info("解码器开始解码");
            // 检查是否有足够的数据来读取整个数据帧
            logger.info("数据长度：" + in.readableBytes());
            if (in.readableBytes() < FRAME_LENGTH) {
                logger.info("数据长度为{}，少于数据帧长度需要等待更多数据", in.readableBytes());
                return; // 如果数据不够，等待更多数据
            }

            // 标记当前读取位置
            in.markReaderIndex();

            // 检查帧头
            byte frameHead = in.readByte();
            if (frameHead != FRAME_HEAD) {
                logger.info("帧头{}不匹配0xAD，丢弃该帧", String.format("%02x", frameHead));
                return;
            }
            // 读取数据长度
            int dataLength = in.readUnsignedByte(); // 读取 DATA[2]
            if (dataLength != 0xFC) {
                logger.info("数据长度{}不符和0xFC，丢弃该帧", String.format("%02x", dataLength));
                return;
            }
            byte[] crcData = new byte[249];
            in.readBytes(crcData);

            logger.info("crcData：{}", com.sczc.esm27.collection.core.util.ByteUtil.bytesToHex(crcData));
            byte crcHigh = in.readByte(); // CRC高位
            byte crcLow = in.readByte();  // CRC低位
            byte[] calculatedCRC = calculateCRC16(crcData);
            if (calculatedCRC[0] != crcHigh || calculatedCRC[1] != crcLow) {
                logger.info("数据校验码不符合预期，数据校验码为{}{},正确的值应为{}{}",
                        String.format("%02x", crcHigh),
                        String.format("%02x", crcLow),
                        String.format("%02x", calculatedCRC[0]),
                        String.format("%02x", calculatedCRC[1]));
                in.skipBytes(3);
                return;
            }
            // 检查帧尾
            byte frameTail = in.readByte();
            if (frameTail != FRAME_TAIL) {
                logger.info("帧尾{}不匹配0xFF，丢弃该帧", String.format("%02x", frameTail));
                in.skipBytes(2);
                return;
            }
            // 检查结束符
            byte ending1 = in.readByte();
            byte ending2 = in.readByte();
            if (ending1 != ENDING_1 || ending2 != ENDING_2) {
                logger.info("结束符{}{}不匹配0x0D 0x0A，丢弃该帧", String.format("%02x", ending1), String.format("%02x", ending2));
                return;
            }
            byte cmd = crcData[1];
            if (cmd != 0x01) {
                logger.info("CMD不符合，CMD为{},正确的值应为0x01", String.format("%02x", cmd));
                return;
            }
            byte[] frameBody = new byte[241];
            // 复制数据
            System.arraycopy(crcData, 8, frameBody, 0, 241);
            logger.info("数据体为：{}", com.sczc.esm27.collection.core.util.ByteUtil.bytesToHex(frameBody));
            // 解析数据体
            ESM27ResponseMessageEntity entity = new ESM27ResponseMessageEntity(cmd, frameBody);
            // 将结果添加到输出列表
            out.add(entity);
        } catch (Exception e) {
            logger.error("解析错误:{}", e.getMessage());
            e.printStackTrace();
        }
    }

    // 计算ModBus CRC16校验码
    private byte[] calculateCRC16(byte[] data) {
        int crc = 0xFFFF;
        for (byte b : data) {
            crc ^= (b & 0xFF);
            for (int i = 0; i < 8; i++) {
                if ((crc & 1) != 0) {
                    crc >>= 1;
                    crc ^= 0xA001;
                } else {
                    crc >>= 1;
                }
            }
        }
        // 高8位和低8位
        byte[] result = new byte[2];
        result[0] = (byte) ((crc >> 8) & 0xFF);   // 高8位
        result[1] = (byte) (crc & 0xFF);          // 低8位
        return result;
    }
}
